#include "../include/Model.h"
#include "../include/objLoader.h"
#include "../include/Functions.h"
#include <iostream>

Model::Model(std::string filename) {
    this->filename = filename;
    objLoader *objData = new objLoader();
    objData->load(filename.c_str());
    vertexCount = objData->vertexCount;
    indexCount = 3*objData->faceCount;
    materialCount = objData->materialCount;

    float minX = objData->vertexList[0]->e[0];
    float maxX = objData->vertexList[0]->e[0];
    float minY = objData->vertexList[0]->e[1];
    float maxY = objData->vertexList[0]->e[1];
    float minZ = objData->vertexList[0]->e[2];
    float maxZ = objData->vertexList[0]->e[2];
    for(int i = 1; i < vertexCount; i++) {
        float x = objData->vertexList[i]->e[0];
        float y = objData->vertexList[i]->e[1];
        float z = objData->vertexList[i]->e[2];
        if(x < minX) minX = x;
        if(x > maxX) maxX = x;
        if(y < minY) minY = y;
        if(y > maxY) maxY = y;
        if(z < minZ) minZ = z;
        if(z > maxZ) maxZ = z;
    }
    width = maxX - minX;
    height = maxY - minY;
    depth = maxZ - minZ;
    vec3 center;
    center.x = minX + (width/2);
    center.y = minY + (height/2);
    center.z = minZ + (depth/2);


    Vertex *vertices = new Vertex[vertexCount];
    for(int i = 0; i < vertexCount; i++) {
        vertices[i].normal[0] = 0;
        vertices[i].normal[1] = 0;
        vertices[i].normal[2] = 0;
        vertices[i].position[0] = objData->vertexList[i]->e[0]-center.x;
        vertices[i].position[1] = objData->vertexList[i]->e[1]-center.y;
        vertices[i].position[2] = objData->vertexList[i]->e[2]-center.z;
        vertices[i].texCoords[0] = 0;
        vertices[i].texCoords[1] = 0;
    }

    indexID = new GLuint[materialCount];
    texID = new GLuint[materialCount];
    iCounts = new int[materialCount];
    ambient = new vec4[materialCount];
    diffuse = new vec4[materialCount];
    specular = new vec4[materialCount];
    shiny = new GLuint[materialCount];

    for(int i = 0; i < materialCount; i++) {
        int iCount = 0;
        //Count number of indices for this material
        for(int j = 0; j < objData->faceCount; j++) {
            if(objData->faceList[j]->material_index == i) {
                iCount += 3;
            }
        }
        iCounts[i] = iCount;
        GLuint *indices = new GLuint[iCount];
        for(int j = 0, ii = 0; j < objData->faceCount; j++) {
            if(objData->faceList[j]->material_index == i) {
                GLuint v1 = objData->faceList[j]->vertex_index[0];
                GLuint v2 = objData->faceList[j]->vertex_index[1];
                GLuint v3 = objData->faceList[j]->vertex_index[2];
                indices[ii] = v1;
                indices[ii+1] = v2;
                indices[ii+2] = v3;
                ii += 3;
                vertices[v1].normal[0] = objData->normalList[objData->faceList[j]->normal_index[0]]->e[0];
                vertices[v1].normal[1] = objData->normalList[objData->faceList[j]->normal_index[0]]->e[1];
                vertices[v1].normal[2] = objData->normalList[objData->faceList[j]->normal_index[0]]->e[2];
                vertices[v1].texCoords[0] = objData->textureList[objData->faceList[j]->texture_index[0]]->e[0];
                vertices[v1].texCoords[1] = 1.0 - objData->textureList[objData->faceList[j]->texture_index[0]]->e[1];
                vertices[v2].normal[0] = objData->normalList[objData->faceList[j]->normal_index[1]]->e[0];
                vertices[v2].normal[1] = objData->normalList[objData->faceList[j]->normal_index[1]]->e[1];
                vertices[v2].normal[2] = objData->normalList[objData->faceList[j]->normal_index[1]]->e[2];
                vertices[v2].texCoords[0] = objData->textureList[objData->faceList[j]->texture_index[1]]->e[0];
                vertices[v2].texCoords[1] = 1.0 - objData->textureList[objData->faceList[j]->texture_index[1]]->e[1];
                vertices[v3].normal[0] = objData->normalList[objData->faceList[j]->normal_index[2]]->e[0];
                vertices[v3].normal[1] = objData->normalList[objData->faceList[j]->normal_index[2]]->e[1];
                vertices[v3].normal[2] = objData->normalList[objData->faceList[j]->normal_index[2]]->e[2];
                vertices[v3].texCoords[0] = objData->textureList[objData->faceList[j]->texture_index[2]]->e[0];
                vertices[v3].texCoords[1] = 1.0 - objData->textureList[objData->faceList[j]->texture_index[2]]->e[1];
            }
        }

        //std::cout << objData->materialList[i]->name << std::endl;
        if(objData->materialList[i]->texture_filename != "") {
            texID[i] = CG::loadTexture(objData->materialList[i]->texture_filename);
        } else {
            texID[i] = NULL;
        }

        ambient[i].x = objData->materialList[i]->amb[0];
        ambient[i].y = objData->materialList[i]->amb[1];
        ambient[i].z = objData->materialList[i]->amb[2];
        ambient[i].w = objData->materialList[i]->amb[3];
        diffuse[i].x = objData->materialList[i]->diff[0];
        diffuse[i].y = objData->materialList[i]->diff[1];
        diffuse[i].z = objData->materialList[i]->diff[2];
        diffuse[i].w = objData->materialList[i]->diff[3];
        specular[i].x = objData->materialList[i]->spec[0];
        specular[i].y = objData->materialList[i]->spec[1];
        specular[i].z = objData->materialList[i]->spec[2];
        specular[i].w = objData->materialList[i]->spec[3];
        shiny[i] = (GLuint)objData->materialList[i]->shiny;

        //Send index data to GPU
        glGenBuffers(1, indexID+i);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexID[i]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*iCount, NULL, GL_STATIC_DRAW);
        glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint)*iCount, indices);
        delete[] indices;
    }

//    GLuint *indices = new GLuint[indexCount];
//    for(GLuint i = 0, ii = 0; i < objData->faceCount; i++, ii+=3) {
//        GLuint v1 = objData->faceList[i]->vertex_index[0];
//        GLuint v2 = objData->faceList[i]->vertex_index[1];
//        GLuint v3 = objData->faceList[i]->vertex_index[2];
//        indices[ii] = v1;
//        indices[ii+1] = v2;
//        indices[ii+2] = v3;
//        vertices[v1].normal[0] = objData->normalList[objData->faceList[i]->normal_index[0]]->e[0];
//        vertices[v1].normal[1] = objData->normalList[objData->faceList[i]->normal_index[0]]->e[1];
//        vertices[v1].normal[2] = objData->normalList[objData->faceList[i]->normal_index[0]]->e[2];
//        vertices[v1].texCoords[0] = objData->textureList[objData->faceList[i]->texture_index[0]]->e[0];
//        vertices[v1].texCoords[1] = objData->textureList[objData->faceList[i]->texture_index[0]]->e[1];
//        vertices[v2].normal[0] = objData->normalList[objData->faceList[i]->normal_index[1]]->e[0];
//        vertices[v2].normal[1] = objData->normalList[objData->faceList[i]->normal_index[1]]->e[1];
//        vertices[v2].normal[2] = objData->normalList[objData->faceList[i]->normal_index[1]]->e[2];
//        vertices[v2].texCoords[0] = objData->textureList[objData->faceList[i]->texture_index[1]]->e[0];
//        vertices[v2].texCoords[1] = objData->textureList[objData->faceList[i]->texture_index[1]]->e[1];
//        vertices[v3].normal[0] = objData->normalList[objData->faceList[i]->normal_index[2]]->e[0];
//        vertices[v3].normal[1] = objData->normalList[objData->faceList[i]->normal_index[2]]->e[1];
//        vertices[v3].normal[2] = objData->normalList[objData->faceList[i]->normal_index[2]]->e[2];
//        vertices[v3].texCoords[0] = objData->textureList[objData->faceList[i]->texture_index[2]]->e[0];
//        vertices[v3].texCoords[1] = objData->textureList[objData->faceList[i]->texture_index[2]]->e[1];
//    }

    //Send vertex data to GPU
    glGenBuffers(1, &vboID);
    glBindBuffer(GL_ARRAY_BUFFER, vboID);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertexCount, NULL, GL_STATIC_DRAW);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex)*vertexCount, vertices);

//    //Send index data to GPU
//    glGenBuffers(1, &indexID);
//    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexID);
//    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*indexCount, NULL, GL_STATIC_DRAW);
//    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint)*indexCount, indices);
    delete[] vertices;
//    delete[] indices;
//    delete objData;
}

void Model::render(GLuint shaderProg) {
    glUseProgram(shaderProg);

    glBindBuffer(GL_ARRAY_BUFFER, vboID);

    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    glNormalPointer(GL_FLOAT, sizeof(Vertex),  BUFFER_OFFSET(12));
    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0));

    glClientActiveTexture(GL_TEXTURE0);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(24));

    glEnable(GL_TEXTURE_2D);
    for(int i = 0; i < materialCount; i++) {
        glMaterialfv(GL_FRONT, GL_AMBIENT, (GLfloat*)(ambient+i));
        glMaterialfv(GL_FRONT, GL_DIFFUSE, (GLfloat*)(diffuse+i));
        glMaterialfv(GL_FRONT, GL_SPECULAR, (GLfloat*)(specular+i));
        glMateriali(GL_FRONT, GL_SHININESS, shiny[i]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexID[i]);
        if(texID[i] != NULL) {
            CG::bindTexture(texID[i], GL_TEXTURE0);
            CG::setTextureUnit(shaderProg, "tex", 0);
        }
        glDrawElements(GL_TRIANGLES, iCounts[i], GL_UNSIGNED_INT, BUFFER_OFFSET(0));
    }

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_TEXTURE_2D);

    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}
